package com.run_java_test.jar_upload_run.controller.v1;

import com.run_java_test.jar_upload_run.services.v1.JarClassRegisterService;
import com.run_java_test.jar_upload_run.services.v1.ProjectJarClassLoader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.List;

@RequestMapping("test_jars")
@RestController
public class JarFileController {

    private final ApplicationContext applicationContext;
    private final JarClassRegisterService jarClassRegisterService;

    @Autowired
    public JarFileController(
            ApplicationContext applicationContext,
            JarClassRegisterService jarClassRegisterService
    ) {
        this.applicationContext = applicationContext;
        this.jarClassRegisterService = jarClassRegisterService;
    }
    // ----------------------------------------------------------------------------------------------------------------

    // 上传jar文件
    @PostMapping("upload_jar")
    public String uploadJar(@RequestParam("file") MultipartFile file) throws Exception {
        // 保存文件到某个目录下面
        File jarFile = this.saveFileToNowProjectDirectory(file);
        // 将上传的文件写入目标文件
        file.transferTo(jarFile);
        // 解析jar文件中的class文件
        ProjectJarClassLoader jarClassLoader = new ProjectJarClassLoader(jarFile.toURI().toURL());
        // 加载以哪个包开头的class文件
        List<Class<?>> classes = jarClassLoader.loadAllClasses("com.run_java_test");

        // 遍历 class 文件 注册为bean
        for (int i = 0; i < classes.size(); i++) {
            Class<?> aClass = classes.get(i);
            // 注册 Spring Bean
            // 注册到 Spring 容器
            if (
                    aClass.isAnnotationPresent(Component.class) ||
                            aClass.isAnnotationPresent(Service.class) ||
                            aClass.isAnnotationPresent(RestController.class) ||
                            aClass.isAnnotationPresent(Controller.class)
            ) {
                this.registerBean(aClass);
            }
        }

        // 遍历 class 文件
        for (int i = 0; i < classes.size(); i++) {
            // 将每个class文件进行注册到spring并且将端点映射到方法上
            jarClassRegisterService.registerController(classes.get(i));
        }
        return "JAR uploaded and classes registered.";
    }

    // 将bean注册到spring容器
    private void registerBean(Class<?> clazz) {
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();

        // 创建并注册 Bean 定义
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(clazz);
        beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
        // 获取类名并将首字母小写
        String beanName = clazz.getSimpleName();
        beanName = Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1);
        beanFactory.registerBeanDefinition(beanName, beanDefinition);

        System.out.println("Registered dynamic Bean: " + beanName);
    }

//    // 先注册
//    private void registerBean(Class<?> clazz) {
//        GenericApplicationContext genericContext = (GenericApplicationContext) applicationContext;
//        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) genericContext.getBeanFactory();
//
//        // 创建 Bean 定义
//        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
//        beanDefinition.setBeanClass(clazz);
//        beanDefinition.setScope("singleton");
//
//        // 注册 Bean
//        registry.registerBeanDefinition(clazz.getSimpleName(), beanDefinition);
//        System.out.println("Registered Bean: " + clazz.getName());
//    }

    // 卸载导入的 jar 中的类
    @PostMapping("unload_jar")
    public String unloadJar() throws Exception {
        List<Class<?>> loadedClasses = ProjectJarClassLoader.getLoadedClasses();
        for (int i = 0; i < loadedClasses.size(); i++) {
            jarClassRegisterService.unregisterController(loadedClasses.get(i));
        }
        return "JAR unloaded successfully.";
    }
    // ----------------------------------------------------------------------------------------------------------------

    // 保存文件到当前目录下面
    private File saveFileToNowProjectDirectory(MultipartFile file) {
        /**
         * 定义存储目录（项目根路径下的 uploads 文件夹）
         * 创建目录到 java_study_springboot_run_java_source_code 下面 sp33_run_jar_project 当前项目同级的目录
         */
        String uploadDirPath = new File("uploads").getAbsolutePath();
        // 构造文件对象
        File uploadDir = new File(uploadDirPath);
        // 如果目录不存在，则创建
        if (!uploadDir.exists()) {
            // 创建多层目录
            uploadDir.mkdirs();
        }
        // 定义存储的目标文件
        File jarFile = new File(uploadDir, file.getOriginalFilename());
        // 输出文件路径
        System.out.println("文件上传到的位置 --- File uploaded to: " + jarFile.getAbsolutePath());
        return jarFile;
    }

    // 保存文件到其他目录下面
    private File saveFileToOtherDirectory(MultipartFile file) {
        // 定义存储路径
        String uploadDirPath = "D:/uploads";
        // 构造文件对象
        File uploadDir = new File(uploadDirPath);
        // 如果目录不存在，则创建
        if (!uploadDir.exists()) {
            // 创建多层目录
            uploadDir.mkdirs();
        }
        // 定义目标文件
        File jarFile = new File(uploadDir, file.getOriginalFilename());
        // 输出文件路径
        System.out.println("文件上传到的位置 --- File uploaded to: " + jarFile.getAbsolutePath());
        return jarFile;
    }

}
